/////////////////////////////////////////////////////////
// Frm_Mesh.cpp
//
//
/////////////////////////////////////////////////////////
#include "Stdafx.h"

#include "frm_mesh.h"

using namespace Frm;

TBuff* TBuff::_Instance = NULL;

/***********************************************
NEW-NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/

Bone::~Bone(void){
	if (_Vertices!=0) delete[] _Vertices;
   if (_Weights!=0) delete[] _Weights;
   while(!_Bones.empty())
   	{delete _Bones.back();_Bones.pop_back();}
}

Bone* Bone::IsName(std::string &BoneName)
{
	if (strcmp(_Name.c_str(), BoneName.c_str())==0)
   	return this;

   if (_Bones.empty())
   	return 0;

   for(std::list<Bone*>::iterator i = _Bones.begin(); i != _Bones.end(); i++)
   	if ((*i)->IsName(BoneName) != 0)
        	return (*i)->IsName(BoneName);
   return 0;
}

void Bone::UpdateIndices(uint16 pIndex)
{
	for (uint32 i = 0; i < _nVertices; i++)
   	_Vertices[i] += pIndex;
}
/***END*****************************************/


Mesh::~Mesh(void)
{
	if (_Vertices!=0) delete[] _Vertices;
   if (_Faces!=0) delete[] _Faces;
   if (_Normals!=0) delete[] _Normals;
   if (_TextureCoords!=0) delete[] _TextureCoords;
   while(!_Materials.empty())
   	{delete _Materials.back();
      _Materials.pop_back();}
   if (_FaceMaterials != 0) delete[] _FaceMaterials;
   while (!_Subsets.empty())
   	{delete[] _Subsets.back()->Faces;
      delete _Subsets.back();
      _Subsets.pop_back();}
}

void Mesh::UpdateIndices(void)
{
	for (uint32 i = 0; i < _nFaces; i++)
   	{
      _Faces[i].data[0] += _FirstVertex;
      _Faces[i].data[1] += _FirstVertex;
      _Faces[i].data[2] += _FirstVertex;

      _FaceMaterials[i] += _FirstMaterial;
      }

   if (_nNormals != 0)
 	 	for (uint32 i = 0; i < _nFaces; i++)
   		{
     	 	_FaceNormals[i].data[0] += _FirstNormal;
      	_FaceNormals[i].data[1] += _FirstNormal;
      	_FaceNormals[i].data[2] += _FirstNormal;
      	}
}

void Mesh::CreateSubsets(void){
	uint32 FaceCount;
   Subset* MeshSubset;

   //For each material
	for (uint32 i=0; i < _Materials.size(); i++)
   	{
      //We count the number of faces using this material
      FaceCount = 0;
   	for (uint32 j = 0; j < _nFaces; j++)
      	if (_FaceMaterials[j]==i)
         	++FaceCount;
      //We initialise the mesh subset
      MeshSubset = new Subset;
      MeshSubset->Size = (uint16)FaceCount;
      MeshSubset->Faces = new Face[FaceCount];
      int k = 0;
      //We fill in the Mesh subset
      for (uint32 j = 0; j < _nFaces; j++)
      	if (_FaceMaterials[j]==i)
         	MeshSubset->Faces[k++] = _Faces[j];
      //And we add that subset to the list
      _Subsets.push_back(MeshSubset);
      }
}

Model3D::~Model3D(void){
   /***********************************************
	NEW-NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/
	if (_Skeletton!=0) delete _Skeletton;
   /***END*****************************************/
   while(!_Meshes.empty()) {
   	delete _Meshes.back(); _Meshes.pop_back();}
}

Mesh* Model3D::IsMeshName(std::string &pText){
	for(std::list<Mesh*>::iterator i = _Meshes.begin(); i != _Meshes.end(); i++)
     	if ((*i)->IsName(pText) != 0)
        	return (*i)->IsName(pText);
   return 0;
}

void Model3D::ConcatenateMeshes(void){
	//We only concatenate when there are at least two meshes
	if (_Meshes.size() < 2)
   	return;

   MYTRACE("Concatenating Meshes ...");

   Mesh* ConcatMesh = new Mesh;
   Mesh* LastMesh = _Meshes.back();

   ConcatMesh->_Name = "ConcatMesh";

   //We create the new mesh.
   //We get the dimensions of the new mesh
   ConcatMesh->_nVertices = LastMesh->_FirstVertex + LastMesh->_nVertices;
   ConcatMesh->_nFaces = LastMesh->_FirstFace + LastMesh->_nFaces;
   ConcatMesh->_nTextureCoords = LastMesh->_FirstTextureCoord + LastMesh->_nTextureCoords;
   ConcatMesh->_nNormals = LastMesh->_FirstNormal + LastMesh->_nNormals;

   //Here we have a control:
   //Texture coordinates must be as numerous as Vertices or there must be none
   //Normal vectors must be as numerous as Vertices or there must be none
   if ((ConcatMesh->_nTextureCoords < ConcatMesh->_nVertices) && (ConcatMesh->_nTextureCoords != 0))
     	ConcatMesh->_nTextureCoords = ConcatMesh->_nVertices;
   if ((ConcatMesh->_nNormals < ConcatMesh->_nVertices) && (ConcatMesh->_nNormals != 0))
     	ConcatMesh->_nNormals = ConcatMesh->_nVertices;

   MYTRACE("Final number of Vertices:", ConcatMesh->_nVertices);
   MYTRACE("Final number of Faces:", ConcatMesh->_nFaces);
   MYTRACE("Final number of TextureCoords:", ConcatMesh->_nTextureCoords);
   MYTRACE("Final number of Normals:", ConcatMesh->_nNormals);
   MYTRACE("Final number of Materials:", ConcatMesh->_nMaterials);

   //We create all the arrays:
   // - Vertices and Faces
   // - Material indices per face
   // - Texture Coords
   // - Normal vectors and Face Allocation of Normal vectors
   ConcatMesh->_Vertices = new Frm::Vertex[ConcatMesh->_nVertices];
   memset(ConcatMesh->_Vertices, 0, ConcatMesh->_nVertices * sizeof(Frm::Vertex));
   ConcatMesh->_Faces = new Frm::Face[ConcatMesh->_nFaces];
   memset(ConcatMesh->_Faces, 0, ConcatMesh->_nFaces * sizeof(Frm::Face));
   ConcatMesh->_FaceMaterials = new uint16[ConcatMesh->_nFaces];
   memset(ConcatMesh->_FaceMaterials, 0, ConcatMesh->_nFaces * sizeof(uint16));
   if (ConcatMesh->_nTextureCoords != 0)
   	{
	   ConcatMesh->_TextureCoords = new Frm::TCoord[ConcatMesh->_nTextureCoords];
   	memset(ConcatMesh->_TextureCoords, 0, ConcatMesh->_nTextureCoords * sizeof(Frm::TCoord));
      }
   if (ConcatMesh->_nNormals != 0)
   	{
		ConcatMesh->_Normals = new Frm::vector<float>[ConcatMesh->_nNormals];
   	ConcatMesh->_FaceNormals = new Frm::Face[ConcatMesh->_nFaces];
   	memset(ConcatMesh->_Normals, 0, ConcatMesh->_nNormals * sizeof(Frm::vector<float>));
   	memset(ConcatMesh->_FaceNormals, 0, ConcatMesh->_nFaces * sizeof(Frm::Face));
      }

   //We fill up the arrays with each array from the _Meshes container
	for (std::list<Mesh*>::iterator i = _Meshes.begin(); i!= _Meshes.end(); i++)
   	{
      (*i)->UpdateIndices();
      memcpy(&(ConcatMesh->_Vertices[(*i)->_FirstVertex]),(*i)->_Vertices, (*i)->_nVertices * sizeof(Frm::Vertex));
      memcpy(&(ConcatMesh->_Faces[(*i)->_FirstFace]),(*i)->_Faces, (*i)->_nFaces * sizeof(Frm::Face));
      memcpy(&(ConcatMesh->_FaceMaterials[(*i)->_FirstFace]),(*i)->_FaceMaterials, (*i)->_nFaces * sizeof(uint16));
      if ((*i)->_nTextureCoords != 0)
	      memcpy(&(ConcatMesh->_TextureCoords[(*i)->_FirstTextureCoord]),(*i)->_TextureCoords, (*i)->_nTextureCoords * sizeof(Frm::TCoord));
      if ((*i)->_nNormals != 0)
      	{
      	memcpy(&(ConcatMesh->_Normals[(*i)->_FirstNormal]),(*i)->_Normals, (*i)->_nNormals * sizeof(Frm::vector<float>));
			memcpy(&(ConcatMesh->_FaceNormals[(*i)->_FirstFace]),(*i)->_FaceNormals, (*i)->_nFaces * sizeof(Frm::Face));
         }
      while (!(*i)->_Materials.empty())
      	{
         ConcatMesh->_Materials.push_back((*i)->_Materials.front());
         (*i)->_Materials.pop_front();
         }
      }

   /***********************************************
	NEW-NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/

   ////////////////////////////////////////////////////////////
   //OK. We now process the bone hierarchy to update the
   //skinning indices

   MYTRACE("Adapting the Bone hierarchy ...");
   if (_Skeletton != 0)
   	UpdateBoneIndices(_Skeletton);

   MYTRACE("Bone hierarchy adapted.");
   /***END*****************************************/

   //We eventually delete all the previous meshes
	while(!_Meshes.empty()) {
   	delete _Meshes.back(); _Meshes.pop_back();}

   //and push the new concatenated one
   _Meshes.push_back(ConcatMesh);

   //We create the subsets
   ConcatMesh->CreateSubsets();

   MYTRACE("All meshes are concatenated.");
}

/***********************************************
NEW-NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/
void Model3D::UpdateBoneIndices(Bone* &pBone)
{
	Mesh* BoneMesh = IsMeshName(pBone->_MeshName);
   if (BoneMesh != 0)
   	pBone->UpdateIndices(BoneMesh->_FirstVertex);
	pBone->_MeshName = "ConcatMesh";
   if (!pBone->_Bones.empty())
   	for (std::list<Bone*>::iterator i = pBone->_Bones.begin(); i!= pBone->_Bones.end(); i++)
      	UpdateBoneIndices(*i);
}
/***END*****************************************/


